package com.blqy.fr.common.aspect;

import com.blqy.fr.common.annotation.BlQyLogin;
import com.blqy.fr.common.constant.RedisConstant;
import com.blqy.fr.common.execption.BlQyException;
import com.blqy.fr.common.result.ResultCodeEnum;
import com.blqy.fr.common.util.AuthContextHolder;
import com.blqy.fr.vo.user.UserVo;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect // 要想成为注解，这个注解不能少
@Component // 会被Ioc容器扫描到，让容器来管理
public class BlQyLoginAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    // 1.必须有返回值，2.必须有参数 3.必须手动执行目标方法 4.抛出异常
    @Around("@annotation(blQyLogin)")  // 只切带注解的方法
    public Object around(ProceedingJoinPoint joinPoint, BlQyLogin blQyLogin) throws Throwable {
        // 1. 从request头信息中获取token
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String token = request.getHeader("token");

        // 定义登录用户
        UserVo userVo = null;
        // 2.判断token是否为空
        if (StringUtils.isNotBlank(token)) {
            // 3.查询redis中的登录状态
            userVo = (UserVo) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
        }
        // 4.判断是否已登录，如果未登录并且是必须登录情况下则抛出异常
        if (userVo == null && blQyLogin.required()/*默认值为true,切当前方法*/) {
            throw new BlQyException(ResultCodeEnum.LOGIN_AUTH);
        }
        // 5.如果已登录则放入ThreadLocal
        if (userVo != null) {
            AuthContextHolder.setUserId(userVo.getId());
            AuthContextHolder.setUsername(userVo.getNickName());
        }
        // 执行目标方法业务,使用原始参数,继续执行被 @YuMaLogin 注解标记的目标方法,并等待它的执行结果,将结果保存到 result 中
        Object result = joinPoint.proceed(joinPoint.getArgs());
        // 释放ThreadLocal中的载荷，避免内存泄露
        AuthContextHolder.removeUserId();
        AuthContextHolder.removeUsername();
        AuthContextHolder.removeTeamId();
        return result;
    }

    @Pointcut("execution(* com.blqy.fr.*.service.*.*(..))")
    public void point() {
    }

    @Before("point()")
    public void before() {
        System.out.println("----------------before--------------------");
    }

    @After("point()")
    public void after() {
        System.out.println("----------------after--------------------");
    }
}